package org.example.Controller;

import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.service.IRedisService;
//import org.example.stream.RedisStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.query.SortQuery;
import org.springframework.data.redis.core.query.SortQueryBuilder;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.data.redis.hash.HashMapper;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import sun.nio.cs.UTF_32BE;

import java.nio.charset.Charset;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.zip.DeflaterOutputStream;

import static io.netty.util.CharsetUtil.US_ASCII;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.stream.Collectors.toList;

/**
 * https://blog.csdn.net/weixin_44972575/article/details/107107489
 * https://www.jb51.net/article/250587.htm
 */

@RestController
public class RedisController {
    @Autowired
    @Qualifier("redisTemplate2")
    private RedisTemplate<String, Object> redisTemplate;


    private static final Logger log = LoggerFactory.getLogger(RedisController.class);

    @Autowired
    private IRedisService redisService;

//    @Autowired
//    private RedisStream redisStream;

//    @Autowired
//    private RedisTemplate redisTemplate;



    /**
     * 设置过期时间10分钟
     * @param key
     */
    @RequestMapping("/setT")
    public void setT(String key) {
        redisTemplate.opsForValue().set(key,2L,10,TimeUnit.MINUTES);
    }

    @RequestMapping("/setD")
    public void setD(String key) {
        redisTemplate.opsForValue().set(key,2L,Duration.ofMillis(40000L));
    }



    @RequestMapping("/set")
    public void set(String key,String value) {
        redisTemplate.opsForValue().set(key,value);
    }



    /**
     * 设置,如果缺席,如果没有，做set操作，返回true,否则返回false
     * @param key
     * @return
     */
    @RequestMapping("/setIfAbsent")
    public Boolean setIfAbsent(String key) {
        return redisTemplate.opsForValue().setIfAbsent(key,2L);
    }


    /**
     * 设置,如果缺席,如果没有，做set操作，返回true,否则返回false
     * @param key
     * @return
     */
    @RequestMapping("/setIfAbsentD")
    public Boolean setIfAbsentD(String key) {
        return redisTemplate.opsForValue().setIfAbsent(key,2L, Duration.ofMillis(100000));
    }

    /**
     * 设置,如果缺席,如果没有，做set操作，返回true,否则返回false
     * @param key
     * @return
     */
    @RequestMapping("/setIfAbsentM")
    public Boolean setIfAbsentM(String key) {
        return redisTemplate.opsForValue().setIfAbsent(key,13L,100, TimeUnit.MINUTES);
    }


    /**
     * 仅当key存在的时候才会执行set指令，并返回true,否则返回false
     * @param key
     * @return
     */


    @RequestMapping("/setIfPresent")
    public Boolean setIfPresent(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().setIfPresent(key,12L);
    }
    /**
     * 仅当key存在的时候才会执行set指令，并返回true,否则返回false
     * @param key
     * @return
     *
     * 127.0.0.1:6379> set hello world
     * OK
     * 127.0.0.1:6379> set hello newval xx    # 由于hello存在，所以set命令会设置成功。
     * OK
     * 127.0.0.1:6379> get hello              # 可以获取到新值
     * "newval"
     *
     * 127.0.0.1:6379> set newkey val xx      # 由于newkey不存在，所以不会设置成功
     * (nil)
     * 127.0.0.1:6379> get newkey
     * (nil)
     *
     */

    @RequestMapping("/setIfPresentT")
    public Boolean setIfPresentT(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().setIfPresent(key,12L,2,TimeUnit.MINUTES);
    }

    /**
     * 仅当key存在的时候才会执行set指令，并返回true,否则返回false
     * @param key
     * @return
     */
    @RequestMapping("/setIfPresentD")
    public Boolean setIfPresentD(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().setIfPresent(key,12L,Duration.ofMinutes(30000L));
    }




    /**
     * 插入多重集合
     * @param key
     */
    @RequestMapping("/multiSet")
    public void multiSet(String key) {
        Map valueMap = new HashMap();
        valueMap.put("valueMap1","map1");
        valueMap.put("valueMap2","map2");
        valueMap.put("valueMap3","map3");
        redisTemplate.opsForValue().multiSet(valueMap);
    }


    /**如果不存在，则设置多个，
     * 必须是{值}1 不然会报错CROSSSLOT Keys in request don't hash to the same slot
     *
     *
     * @param key
     * @return
     */
    @RequestMapping("/multiSetIfAbsent")
    public Boolean multiSetIfAbsent(String key) {
        Map valueMap = new HashMap();
        valueMap.put("{valueMap}1","map1");
        valueMap.put("{valueMap}2","map2");
        valueMap.put("{valueMap}3","map3");
        return redisTemplate.opsForValue().multiSetIfAbsent(valueMap);
    }


    @RequestMapping("/get")
    public Object get(String key) {
        return  redisTemplate.opsForValue().get(key);

    }

    /**
     *获取并设置,设置键的值并返回其旧值。
     *
     * @param key
     * @return
     */
    @RequestMapping("/getAndSet")
    public Object getAndSet(String key) {
        return  redisTemplate.opsForValue().getAndSet(key, 102L);
    }


    /**
     * 获得多个key
     * @param key
     * @return
     */
    @RequestMapping("/multiGet")
    public List<Object> multiGet(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().multiGet(collect);
    }

    /**
     * 从1开始加，每次都加1，然后每次都返回当前最新的值
     * @param key
     * @return
     */
    @RequestMapping("/increaseCounter")
    public Long increaseCounter(String key) {
        return redisTemplate.opsForValue().increment(key);
    }





    /**
     * 从50开始加，每次都加50，然后每次都返回当前最新的值
     * @param key
     * @return
     */
    @RequestMapping("/increaseCounter1")
    public Long increaseCounter1(String key) {
        return redisTemplate.opsForValue().increment(key,50);
    }

    @RequestMapping("/increaseDouble")
    public Double increaseDouble(String key) {
        return redisTemplate.opsForValue().increment(key,50.5);
    }


    /**
     * 做减法-1
     * @param key
     * @return
     */
    @RequestMapping("/decrement")
    public Long decrement(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }

    /**
     * 做减法-2
     * @param key
     * @return
     */
    @RequestMapping("/decrementValue")
    public Long decrementValue(String key) {
        return redisTemplate.opsForValue().decrement(key,2);
    }


    /**
     * 对key的value进行追加,返回总长度
     * @param key
     * @return
     */
    @RequestMapping("/append")
    public Integer append(String key) {
        return redisTemplate.opsForValue().append(key,"value");
    }



    /**
     * 截取2-100位的数据
     * @param key
     * @return
     */
    @RequestMapping("/getLong")
    public String getLong(String key) {
        return redisTemplate.opsForValue().get(key,2,100);
    }

    /**
     * 会占用七个空格
     * □□□□□□□12
     * @param key
     */
    @RequestMapping("/setL")
    public void setL(String key) {
        redisTemplate.opsForValue().set(key,12L,7L);
    }

    /**
     * 求出当前key对应的长度
     * @param key
     * @return
     */
    @RequestMapping("/size")
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }


    /**
     * https://blog.csdn.net/qulinchao/article/details/122190636
     * RedisCallback和SessionCallBack：
     * 1.作用: 让RedisTemplate进行回调，通过他们可以在同一条连接中一次执行多个redis命令。
     * 2.SessionCalback提供了良好的封装，优先使用它。
     * 3.RedisCallback使用的是原生RedisConnection，用起来比较麻烦，可读性差，但原生api提供的功能比较齐全。
     * ————————————————
     * 版权声明：本文为CSDN博主「yzh_1346983557」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
     * 原文链接：https://blog.csdn.net/yzh_1346983557/article/details/119837981
     * @param key
     * @return
     */
    @RequestMapping("/execute")
    public Long execute(String key) {
        //此方法需要传入的参数时一个RedisCallback对象实例，实际是一个Redis连接成功的回调方法，一般都是以下方式去调用的
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {

                return connection.del(new byte[][]{"redis-key".getBytes()});
            }
        });
    }

    @RequestMapping("/executeSessionCallback")
    public Integer executeSessionCallback(String key) {
        return redisTemplate.opsForValue().getOperations().execute(new SessionCallback<Integer>() {
            @Override
            public <K, V> Integer execute(RedisOperations<K, V> operations) throws DataAccessException {

                return 1;
            }
        });
    }

    /**
     * 执行流水线，专门做批量处理的
     * @param key
     * @return
     */

    @RequestMapping("/executePipelinedRedisCallback")
    public Object executePipelinedRedisCallback(String key) {
        return redisTemplate.opsForValue().getOperations().executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                return null;
            }
        });
    }


    @RequestMapping("/executePipelinedSeri")
    public List<Object> executePipelinedSeri(String key) {
        return redisTemplate.opsForValue().getOperations().executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                return null;
            }
        },new Jackson2JsonRedisSerializer<Object>(Object.class));
    }

    @RequestMapping("/executePipelinedSessionCallback")
    public List<Object> executePipelinedSessionCallback(String key) {
        return redisTemplate.opsForValue().getOperations().executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                return null;
            }
        });
    }


    @RequestMapping("/executePipelined")
    public List<Object> executePipelined(String key) {
        return redisTemplate.opsForValue().getOperations().executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                return null;
            }
        },new Jackson2JsonRedisSerializer<Object>(Object.class));
    }


    /**
     * 编写redis RedisScript脚本使用
     * @param key
     * @return
     */
    @RequestMapping("/executeRedisScript")
    public String executeRedisScript(String key) {
        return redisTemplate.opsForValue().getOperations().execute(new RedisScript<String>() {
            @Override
            public String getSha1() {
                return null;
            }

            @Override
            public Class<String> getResultType() {
                return null;
            }

            @Override
            public String getScriptAsString() {
                return null;
            }
        },new ArrayList<>());
    }


    @RequestMapping("/executeRedisScript1")
    public String executeRedisScript1(String key) {
        return redisTemplate.opsForValue().getOperations().execute(new RedisScript<String>() {
            @Override
            public String getSha1() {
                return null;
            }

            @Override
            public Class<String> getResultType() {
                return null;
            }

            @Override
            public String getScriptAsString() {
                return null;
            }
        },new Jackson2JsonRedisSerializer<String>(String.class),new Jackson2JsonRedisSerializer<String>(String.class),new ArrayList<>());
    }



    @RequestMapping("/executeWithStickyConnection")
    public  HashSet<String> executeWithStickyConnection(String pattern, int pageSize) {
//        return redisTemplate.opsForValue().getOperations().executeWithStickyConnection(new RedisCallback<Closeable>() {
//            @Override
//            public Closeable doInRedis(RedisConnection connection) throws DataAccessException {
//                return null;
//            }
//        });
        HashSet<String> set = new HashSet<>();
        ScanOptions options = ScanOptions.scanOptions().match(pattern+"*").count(pageSize).build();
        RedisSerializer<?> redisSerializer = redisTemplate.getKeySerializer();
        Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection ->
                new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
        while (cursor.hasNext()) {
            set.add((String) cursor.next());
        }
        try {
            cursor.close();
        } catch (Exception e) {
            System.out.println("关闭 redis connection 失败");
        }
        return set;            //关闭cursor

    }



    @RequestMapping("/hasKey")
    public Object hasKey(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.hasKey(key);
    }

    /**
     * 当前key集合，在redis中存在的数量
     * @param key
     * @return
     */
    @RequestMapping("/countExistingKeys")
    public Long countExistingKeys(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().getOperations().countExistingKeys(collect);
    }

    /**
     * 删除操作
     * @param key
     * @return
     */
    @RequestMapping("/delete")
    public Boolean delete(String key) {
        return redisTemplate.opsForValue().getOperations().delete(key);
    }

    /**
     * 批量删除多个，返回被删除的数量
     * @param key
     * @return
     */
    @RequestMapping("/deleteList")
    public Long deleteList(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().getOperations().delete(collect);
    }



    /**
     *删除操作，非阻塞
     * @param key
     * @return
     */
    @RequestMapping("/unlinkOne")
    public Boolean unlinkOne(String key) {
        return redisTemplate.opsForValue().getOperations().unlink(key);
    }

    /**
     *删除操作，非阻塞
     * @param key
     * @return
     */
    @RequestMapping("/unlink")
    public Long unlink(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());
        return redisTemplate.opsForValue().getOperations().unlink(collect);
    }



    @RequestMapping("/type")
    public DataType type(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.type(key);
    }


    @RequestMapping("/keys")
    public Object keys(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.keys(key+"*");
    }

    /**
     * 随机取出来一个key
     * @param key
     * @return
     */
    @RequestMapping("/randomKey")
    public Object randomKey(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.randomKey();
    }

    @RequestMapping("/rename")
    public Void rename(String key) {
        redisTemplate.opsForValue().getOperations().rename("set1","newkey");


        return null;
    }

    /**
     * todo 有问题
     * @param key
     * @return
     */
    @RequestMapping("/renameIfAbsent")
    public Object renameIfAbsent(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.renameIfAbsent(key,key+"1");
    }


    /**
     * 设置过期时间,成功true,失败false
     * @param key
     * @return
     */
    @RequestMapping("/expire")
    public Boolean expire(String key) {
        return redisTemplate.opsForValue().getOperations().expire(key,1000L,TimeUnit.MINUTES);
    }



    /**
     * 设置过期时间
     * @param key
     * @return
     */
    @RequestMapping("/expireAt")
    public Object expireAt(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.expireAt(key,new Date());
    }


    /**
     * 将key持久化保存
     * @param key
     * @return
     */
    @RequestMapping("/persist")
    public Object persist(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.persist(key);
    }



    /**
     *
     * @param key
     * @return
     */
    @RequestMapping("/move")
    public Object move(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.move(key,1);
    }


    /**
     * key必须存在，就是把key对应的value转化为byte,但是取出来的时候我是没有
     * 执行Redis dump命令并返回结果。Redis使用非标准的序列化机制，并包含校验和信息，因此返回原始字节，
     * 而不是使用valueSerializer进行反序列化。使用dump的返回值作为恢复的值参数
     * 命令：127.0.0.1@6379 connected!
     * > set sd "hello,dump"
     * OK
     * > DUMP sd
     * \x00\x0ahello,dump\x06\x00n\xb6\xe8\xdcn\xad\x08\xe5
     * > RESTORE sd1 0 "\x00\x0ahello,dump\x06\x00n\xb6\xe8\xdcn\xad\x08\xe5"
     * OK
     * > get sd1
     * hello,dump
     *
     * @param key
     * @return
     */


    @RequestMapping("/dump")
    public String dump(String key) {
        redisTemplate.dump(key);
        Object o = redisTemplate.opsForValue().get(key);
        return o.toString();
    }

    /**
     *
     * 将序列化数据 RESTORE 到另一个键上面（也就是复制）
     *> set sd "hello,dump"
     *       OK
     *       > DUMP sd
     *       \x00\x0ahello,dump\x06\x00n\xb6\xe8\xdcn\xad\x08\xe5
     *       > RESTORE sd1 0 "\x00\x0ahello,dump\x06\x00n\xb6\xe8\xdcn\xad\x08\xe5"
     *       OK
     *       > get sd1
     *
     * @param key
     */
    @RequestMapping("/restore")
    public void restore(String key) {
        //key count必须存在
        byte[] bytes = redisTemplate.dump("count");
        redisTemplate.opsForValue().getOperations().restore(key,bytes,10L,TimeUnit.HOURS);
        Object o = redisTemplate.opsForValue().get(key);
        System.out.println(o.toString());
    }

    /**
     * 为true的时候，已经存在的key不可以，会报错
     * @param key
     */
    @RequestMapping("/restoreTrue")
    public void restoreTrue(String key) {
        //key count必须存在
        byte[] bytes = redisTemplate.dump("count");
        redisTemplate.opsForValue().getOperations().restore(key,bytes,10L,TimeUnit.HOURS,Boolean.TRUE);
        Object o = redisTemplate.opsForValue().get(key);
        System.out.println(o.toString());
    }

    /**
     * 为false的时候，已经存在的key可以，不会报错
     * @param key
     */
    @RequestMapping("/restoreFalse")
    public void restoreFalse(String key) {
        //key count必须存在
        byte[] bytes = redisTemplate.dump("count");
        redisTemplate.opsForValue().getOperations().restore(key,bytes,10L,TimeUnit.HOURS,Boolean.FALSE);
        Object o = redisTemplate.opsForValue().get(key);
        System.out.println(o.toString());
    }


    /**
     * 获取过期时间
     * @param key
     * @return
     */
    @RequestMapping("/getExpire")
    public Object getExpire(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.getExpire(key);
    }


    /**
     * 获得过期时间
     * @param key
     * @return
     */
    @RequestMapping("/getExpireTime")
    public Object getExpireTime(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

        return redisTemplate.getExpire(key,TimeUnit.MINUTES);
    }


    /**
     * todo sort在ListAndSetAndZset中查看
     */


    /**监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断
     * 也就是说监听当前的key,在事务的提交阶段，该key没有被其他线程修改值，该事务才会成功提交
     * https://blog.csdn.net/weixin_43493520/article/details/107139313
     * https://blog.csdn.net/bin929/article/details/118705619
     * @param key
     */
    @RequestMapping("/watch")
    public void watch(String key) {

        Runnable runable = new Runnable() {
            @Override
            public void run() {
                String key = "count";
                extracted(key);
            }
        };

        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runable, "thread-" + (i + 1));
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }




    }

    private void extracted(String key) {
        List<Object> result =null;

//        do{
        redisTemplate.setEnableTransactionSupport(true);//不可少
        int count = 0;
        redisTemplate.watch(key); //watch某个key,当该key被其它客户端改变时,则会中断当前的操作

        String value = (String) redisTemplate.opsForValue().get(key);
        if (!StringUtils.isEmpty(value)) {
            count = Integer.parseInt(value);
        }
        count = count + 1;

        redisTemplate.multi(); //开始事务
        redisTemplate.opsForValue().set(key, String.valueOf(count));

        try{
            result = redisTemplate.exec();//执行事务
            System.out.println("result="+result);
        }catch(Exception e){
            //如果key被改变,提交事务时这里会报异常
        }
        //这行格掉就可以复现提交失败的情况，最终数值不一致，不去除就会继续再次重新发起
//        }while(result == null || result.size()<=0);
    }

    private void extracted1(String key) {
        List<Object> result =null;

//        do{
        redisTemplate.setEnableTransactionSupport(true);//不可少
        int count = 0;
        redisTemplate.watch(key); //watch某个key,当该key被其它客户端改变时,则会中断当前的操作

        String value = (String) redisTemplate.opsForValue().get(key);
        if (!StringUtils.isEmpty(value)) {
            count = Integer.parseInt(value);
        }
        count = count + 1;

        redisTemplate.multi(); //开始事务
        redisTemplate.opsForValue().set(key, String.valueOf(count));
        redisTemplate.unwatch();
        try{
            result = redisTemplate.exec();//执行事务
            System.out.println("result="+result);
        }catch(Exception e){
            //如果key被改变,提交事务时这里会报异常
        }
        //这行格掉就可以复现提交失败的情况，最终数值不一致，不去除就会继续再次重新发起
//        }while(result == null || result.size()<=0);
        System.out.println("unwatch");

    }

    /**
     * http://127.0.0.1:8000/watchList?keys=count1&keys=count
     * @param keys
     */
    @RequestMapping("/watchList")
    public void watchList(@RequestParam("keys") List<String> keys) {
        Runnable runable = new Runnable() {
            @Override
            public void run() {
                String key = "count";
                extractedList(key,keys);
            }
        };

        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runable, "thread-" + (i + 1));
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    private void extractedList(String key ,List<String> keys) {
        List<Object> result =null;

//        do{
        redisTemplate.setEnableTransactionSupport(true);//不可少
        int count = 0;
        redisTemplate.watch(keys); //watch某个key,当该key被其它客户端改变时,则会中断当前的操作

        String value = (String) redisTemplate.opsForValue().get(key);
        if (!StringUtils.isEmpty(value)) {
            count = Integer.parseInt(value);
        }
        count = count + 1;

        redisTemplate.multi(); //开始事务
        redisTemplate.opsForValue().set(key, String.valueOf(count));

        try{
            result = redisTemplate.exec();//执行事务
            System.out.println("result="+result);
        }catch(Exception e){
            //如果key被改变,提交事务时这里会报异常
        }
        //这行格掉就可以复现提交失败的情况，最终数值不一致，不去除就会继续再次重新发起
//        }while(result == null || result.size()<=0);
    }
    boolean listItem() {
        // 乐观锁
        redisTemplate.watch("inventory");
        // 判断背包内是否有要出售的物品ItemN，若是没有就结束流程
        if (!redisTemplate.opsForSet().isMember("inventory", "ItemN")) {
            System.out.println("取消监听");
            redisTemplate.unwatch();
            return false;
        }
        // 启用事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        // 添加到商店
        redisTemplate.opsForZSet().add("market:", "ItemN", 9);
        // 从背包中移除
        redisTemplate.opsForSet().remove("inventory", "ItemN");
        // 执行事务
        List<Object> result = redisTemplate.exec();
        System.out.println(result);
        // 判断事务是否为空
        if (result == null || result.size() == 0) return false;
        return true;
    }
    /**
     * 取消监听
     * @param key
     */
    @RequestMapping("/unwatch")
    public void unwatch(String key) {

//        boolean b = listItem();

        Runnable runable = new Runnable() {
            @Override
            public void run() {
//                String key = "count";
//                extracted1(key);
                listItem();
            }
        };

        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runable, "thread-" + (i + 1));
            thread.start();
            threads.add(thread);
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }

    /**
     * 标记事务块的开始
     * @param key
     * @return
     */
    @RequestMapping("/multi")
    public List<Object> multi(String key) {
        //设置启用事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //标记事务块的开始。命令将被排队，然后可以通过调用exec（）执行或使用discard（）回滚
        redisTemplate.multi();
        redisTemplate.opsForValue().set(key, "xds");

        return redisTemplate.opsForValue().getOperations().exec(new Jackson2JsonRedisSerializer<>(Object.class));
    }

    /**
     * 设置事务回滚
     * @param key
     */
    @RequestMapping("/discard")
    public void discard(String key) {
//设置启用事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //标记事务块的开始。命令将被排队，然后可以通过调用exec（）执行或使用discard（）回滚
        redisTemplate.multi();
        redisTemplate.opsForValue().set(key, "xds");
        redisTemplate.discard();

//        redisTemplate.opsForValue().getOperations().exec(new Jackson2JsonRedisSerializer<>(Object.class));

    }

    @RequestMapping("/exec")
    public List<Object> exec(String key) {
//设置启用事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //标记事务块的开始。命令将被排队，然后可以通过调用exec（）执行或使用discard（）回滚
        redisTemplate.multi();
        redisTemplate.opsForValue().set(key, "xds");

        return redisTemplate.opsForValue().getOperations().exec();
    }


    @RequestMapping("/execSeri")
    public List<Object> execSeri(String key) {
//设置启用事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //标记事务块的开始。命令将被排队，然后可以通过调用exec（）执行或使用discard（）回滚
        redisTemplate.multi();
        redisTemplate.opsForValue().set(key, "xds");

        return redisTemplate.opsForValue().getOperations().exec(new Jackson2JsonRedisSerializer<>(Object.class));
    }



    /**
     * 查询redis 客户端连接情况
     * @param key
     * @return
     */
    @RequestMapping("/getClientList")
    public List<RedisClientInfo> getClientList(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.opsForValue().getOperations().getClientList();
    }


    @RequestMapping("/killClient")
    public void killClient(String key) {
        System.out.println(        redisTemplate.opsForValue().getOperations().getClientList().size());
         redisTemplate.opsForValue().getOperations().killClient("127.0.0.1",4597);
        System.out.println(        redisTemplate.opsForValue().getOperations().getClientList().size());

    }

    /**集群环境下  todo 有问题
     * 当前服务器转变为指定服务器的从属服务器
     * @param key
     */
    @RequestMapping("/slaveOf")
    public void slaveOf(String key) {

        redisTemplate.opsForValue().getOperations().slaveOf("192.168.0.1",6379);
    }

    @RequestMapping("/slaveOfNoOne")
    public void slaveOfNoOne(String key) {

        redisTemplate.opsForValue().getOperations().slaveOfNoOne();
    }

    @RequestMapping("/publishMessage1")
    public void publishMessage1(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }


    /**
     * 群集的操作
     * todo 有问题
     * @param
     * @return
     */
    @RequestMapping("/opsForCluster")
    public void opsForCluster() {
        redisTemplate.opsForCluster();
    }

    /**获取地理位置坐标的api，下面的也有，这里就举一个例子
     * zset类型
     * @param key
     * @return
     */
    @RequestMapping("/opsForGeo")
    public Object opsForGeo(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.opsForValue().getOperations().opsForGeo().add(key,new Point(1,2),2);
    }

    @RequestMapping("/boundGeoOps")
    public Object boundGeoOps(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.opsForValue().getOperations().boundGeoOps(key);
    }


    @RequestMapping("/opsForHash")
    public DataType opsForHash(String key) {
        //   新增hashMap值。
        redisTemplate.opsForHash().put("hashValue","map1","map1-1");
        redisTemplate.opsForHash().put("hashValue","map2","map2-2");
        //  获取指定变量中的hashMap值。
        List<Object> hashList = redisTemplate.opsForHash().values("hashValue");
        System.out.println("通过values(H key)方法获取变量中的hashMap值:" + hashList);
        //  获取变量中的键值对。
        Map<Object,Object> map = redisTemplate.opsForHash().entries("hashValue");
        System.out.println("通过entries(H key)方法获取变量中的键值对:" + map);
        //  获取变量中的指定map键是否有值,如果存在该map键则获取值，没有则返回null。
        Object mapValue = redisTemplate.opsForHash().get("hashValue","map1");
        System.out.println("通过get(H key, Object hashKey)方法获取map键的值:" + mapValue);
        //      判断变量中是否有指定的map键。
        boolean hashKeyBoolean = redisTemplate.opsForHash().hasKey("hashValue","map3");
        System.out.println("通过hasKey(H key, Object hashKey)方法判断变量中是否存在map键:" + hashKeyBoolean);
        //       获取变量中的键。
        Set<Object> keySet = redisTemplate.opsForHash().keys("hashValue");
        System.out.println("通过keys(H key)方法获取变量中的键:" + keySet);
        //获取变量的长度。
        long hashLength = redisTemplate.opsForHash().size("hashValue");
        System.out.println("通过size(H key)方法获取变量的长度:" + hashLength);
        //使变量中的键以double值的大小进行自增长。
        double hashIncDouble = redisTemplate.opsForHash().increment("hashInc","map1",3);
        System.out.println("通过increment(H key, HK hashKey, double delta)方法使变量中的键以值的大小进行自增长:" + hashIncDouble);
        //使变量中的键以long值的大小进行自增长。
        long hashIncLong = redisTemplate.opsForHash().increment("hashInc","map2",6);
        System.out.println("通过increment(H key, HK hashKey, long delta)方法使变量中的键以值的大小进行自增长:" + hashIncLong);
        //         以集合的方式获取变量中的值。
        List<Object> list = new ArrayList<Object>();
        list.add("map1");
        list.add("map2");
        List mapValueList = redisTemplate.opsForHash().multiGet("hashValue",list);
        System.out.println("通过multiGet(H key, Collection<HK> hashKeys)方法以集合的方式获取变量中的值:"+mapValueList);
        //   以map集合的形式添加键值对。
        Map newMap = new HashMap();
        newMap.put("map3","map3-3");
        newMap.put("map5","map5-5");
        redisTemplate.opsForHash().putAll("hashValue",newMap);
        map = redisTemplate.opsForHash().entries("hashValue");
        System.out.println("通过putAll(H key, Map<? extends HK,? extends HV> m)方法以map集合的形式添加键值对:" + map);
        //          如果变量值存在，在变量中可以添加不存在的的键值对，如果变量不存在，则新增一个变量，同时将键值对添加到该变量。
        redisTemplate.opsForHash().putIfAbsent("hashValue","map6","map6-6");
        map = redisTemplate.opsForHash().entries("hashValue");
        System.out.println("通过putIfAbsent(H key, HK hashKey, HV value)方法添加不存在于变量中的键值对:" + map);
        //            匹配获取键值对，ScanOptions.NONE为获取全部键对，ScanOptions.scanOptions().match("map1").build()
        //            匹配获取键位map1的键值对,不能模糊匹配。
        Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("hashValue",ScanOptions.scanOptions().match("map1").build());
//Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("hashValue",ScanOptions.NONE);
        while (cursor.hasNext()){
            Map.Entry<Object,Object> entry = cursor.next();
            System.out.println("通过scan(H key, ScanOptions options)方法获取匹配键值对:" + entry.getKey() + "---->" + entry.getValue());
        }
        //   删除变量中的键值对，可以传入多个参数，删除多个键值对。
        redisTemplate.opsForHash().delete("hashValue","map1","map2");
        map = redisTemplate.opsForHash().entries("hashValue");
        System.out.println("通过delete(H key, Object... hashKeys)方法删除变量中的键值对后剩余的:" + map);
        return redisTemplate.type(key);
    }



    @RequestMapping("/boundHashOps")
    public Object boundHashOps(String key) {
        //往指定key中存储 键值
        redisTemplate.boundHashOps("demo").put("1",1);
        // 根据指定key中得键取出值
        System.out.println(redisTemplate.boundHashOps("demo").get("1"));
//根据指定key中得键删除
        redisTemplate.boundHashOps("demo").delete("1");
// 根据指定key取出全部键值对
        Map<Object, Object> entries = redisTemplate.boundHashOps("demo").entries();
        System.out.println(entries);
        // 根据指定key取出所有键
        Set<Object> keys = redisTemplate.boundHashOps("demo").keys();
        System.out.println(keys);
        //批量存储到指定key中
        Map<String,String> map = new HashMap<>();
        map.put("3","zhangsan");
        map.put("4","lisi");
        redisTemplate.boundHashOps("demo").putAll(map);
        //获取指定key得元素长度
        Long size = redisTemplate.boundHashOps("demo").size();
        System.out.println(size);
        //判断指定key中是否存在该键
        System.out.println(redisTemplate.boundHashOps("demo").hasKey("1"));
// 获取指定key中所有键值对得值
        List<Object> values = redisTemplate.boundHashOps("demo").values();
        System.out.println(values);
        //根据指定key中的键 每次重复自增大小 （整型）
        Long increment = redisTemplate.boundHashOps("demo").increment("1", 1);
        System.out.println(increment);
        // 根据指定key中的键 每次重复自增大小 （小数类型）
        Double aDouble = redisTemplate.boundHashOps("demo").increment("1", 1.1);
        System.out.println(aDouble);
        //根据指定key判断键是否存在，存在返回false不新增，不存在则新增键值对返回true
        System.out.println(redisTemplate.boundHashOps("demo").putIfAbsent("7", 5));
        //设置key得到期时间 TimeUnit 设置时间类型（时、分、秒...）
        redisTemplate.boundHashOps("demo").expire(100000000, TimeUnit.MILLISECONDS);
        // 重新命名当前key得名称
//        redisTemplate.boundHashOps("demo").rename("test");
        //获取当前key的存储方式
        System.out.println(redisTemplate.boundHashOps("demo").getType());
        //获取当前key过期时间
        System.out.println(redisTemplate.boundHashOps("demo").getExpire());
        return null;
    }


    /***
     * STring类型
     * 可以加，数据非常多，非常长
     * @param key
     * @return
     */
    @RequestMapping("/opsForHyperLogLog")
    public Object opsForHyperLogLog(String key) {
        List<String> collect = Arrays.stream(key.split(",")).collect(toList());

//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        return redisTemplate.opsForValue().getOperations().opsForHyperLogLog().add(key,123);
    }




    @RequestMapping("/opsForList")
    public DataType opsForList(String key) {
        //  在变量左边添加元素值
        redisTemplate.opsForList().leftPush("list","a");
        redisTemplate.opsForList().leftPush("list","b");
        redisTemplate.opsForList().leftPush("list","c");
        //获取集合指定位置的值。
        String listValue = redisTemplate.opsForList().index("list",1) + "";
        System.out.println("通过index(K key, long index)方法获取指定位置的值:" + listValue);
        //获取指定区间的值。
        List<Object> list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过range(K key, long start, long end)方法获取指定范围的集合值:"+list);
        //     把最后一个参数值放到指定集合的第一个出现中间参数的前面，如果中间参数值存在的话。
        redisTemplate.opsForList().leftPush("list","a","n");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过leftPush(K key, V pivot, V value)方法把值放到指定参数值前面:" + list);
        //  向左边批量添加参数元素。
        redisTemplate.opsForList().leftPushAll("list","w","x","y");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过leftPushAll(K key, V... values)方法批量添加元素:" + list);
        //   以集合的方式向左边批量添加元素。
        List newList = new ArrayList();
        newList.add("o");
        newList.add("p");
        newList.add("q");
        redisTemplate.opsForList().leftPushAll("list",newList);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过leftPushAll(K key, Collection<V> values)方法以集合的方式批量添加元素:" + list);
        //  如果存在集合则添加元素。
        redisTemplate.opsForList().leftPushIfPresent("presentList","o");
        list =  redisTemplate.opsForList().range("presentList",0,-1);
        System.out.println("通过leftPushIfPresent(K key, V value)方法向已存在的集合添加元素:" + list);
        //   向集合最右边添加元素。
        redisTemplate.opsForList().rightPush("list","w");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过rightPush(K key, V value)方法向最右边添加元素:" + list);
        //   向集合中第一次出现第二个参数变量元素的右边添加第三个参数变量的元素值。
        redisTemplate.opsForList().rightPush("list","w","r");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过rightPush(K key, V pivot, V value)方法向最右边添加元素:" + list);
        //   向右边批量添加元素。
        redisTemplate.opsForList().rightPushAll("list","j","k");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过rightPushAll(K key, V... values)方法向最右边批量添加元素:" + list);
        //   以集合方式向右边添加元素。
        newList.clear();
        newList.add("g");
        newList.add("h");
        redisTemplate.opsForList().rightPushAll("list",newList);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过rightPushAll(K key, Collection<V> values)方法向最右边以集合方式批量添加元素:" + list);
        //向已存在的集合中添加元素。
        redisTemplate.opsForList().rightPushIfPresent("presentList","d");
        list =  redisTemplate.opsForList().range("presentList",0,-1);
        System.out.println("通过rightPushIfPresent(K key, V value)方法已存在的集合向最右边添加元素:" + list);
        //获取集合长度
        long listLength = redisTemplate.opsForList().size("list");
        System.out.println("通过size(K key)方法获取集合list的长度为:" + listLength);
        //移除集合中的左边第一个元素。
        Object popValue = redisTemplate.opsForList().leftPop("list");
        System.out.print("通过leftPop(K key)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println(",剩余的元素是:" + list);
        //   移除集合中左边的元素在等待的时间里，如果超过等待的时间仍没有元素则退出。
        popValue = redisTemplate.opsForList().leftPop("presentList",1, TimeUnit.SECONDS);
        System.out.print("通过leftPop(K key, long timeout, TimeUnit unit)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("presentList",0,-1);
        System.out.println(",剩余的元素是:" + list);
        //        移除集合中右边的元素。
        popValue = redisTemplate.opsForList().rightPop("list");
        System.out.print("通过rightPop(K key)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println(",剩余的元素是:" + list);
        //   移除集合中右边的元素在等待的时间里，如果超过等待的时间仍没有元素则退出。
        popValue = redisTemplate.opsForList().rightPop("presentList",1, TimeUnit.SECONDS);
        System.out.print("通过rightPop(K key, long timeout, TimeUnit unit)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("presentList",0,-1);
        System.out.println(",剩余的元素是:" + list);
        //  移除集合中右边的元素，同时在左边加入一个元素。
        popValue = redisTemplate.opsForList().rightPopAndLeftPush("list","12");
        System.out.print("通过rightPopAndLeftPush(K sourceKey, K destinationKey)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println(",剩余的元素是:" + list);
        //  移除集合中右边的元素在等待的时间里，同时在左边添加元素，如果超过等待的时间仍没有元素则退出。
        popValue = redisTemplate.opsForList().rightPopAndLeftPush("presentList","13",1,TimeUnit.SECONDS);
        System.out.println("通过rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit)方法移除的元素是:" + popValue);
        list =  redisTemplate.opsForList().range("presentList",0,-1);
        System.out.print(",剩余的元素是:" + list);
        //  在集合的指定位置插入元素,如果指定位置已有元素，则覆盖，没有则新增，超过集合下标+n则会报错。
        redisTemplate.opsForList().set("list",3,"15");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.print("通过set(K key, long index, V value)方法在指定位置添加元素后:" + list);
        //  从存储在键中的列表中删除等于值的元素的第一个计数事件。count> 0：删除等于从左到右移动的值的第一个元素；count< 0：
        //  删除等于从右到左移动的值的第一个元素；count = 0：删除等于value的所有元素。
        long removeCount = redisTemplate.opsForList().remove("list",0,"w");
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过remove(K key, long count, Object value)方法移除元素数量:" + removeCount);
        System.out.println(",剩余的元素:" + list);
        //  截取集合元素长度，保留长度内的数据。
        redisTemplate.opsForList().trim("list",0,5);
        list =  redisTemplate.opsForList().range("list",0,-1);
        System.out.println("通过trim(K key, long start, long end)方法截取后剩余元素:" + list);
        return redisTemplate.type(key);
    }


    /**
     * redis list 命令
     * https://blog.csdn.net/qq_39071667/article/details/88871283
     * @param key
     * @return
     */

    @RequestMapping("/boundListOps")
    public Object boundListOps(String key) {
        //定义绑定的键
        BoundListOperations boundListOperations = redisTemplate.boundListOps("lk");
        //在绑定的键中添加值
        boundListOperations.leftPush("h");
        boundListOperations.leftPush("i");
        boundListOperations.rightPush("a");
        boundListOperations.rightPush("b");
        //获取绑定键中给定的区间值
        boundListOperations.range(0,-1).forEach(v -> System.out.println("获取绑定的键中的值" + v));
//    获取给定位置的值
        System.out.println("获取特定位置的值：" + boundListOperations.index(0));
//    弹出左边的值,i
        System.out.println("弹出左边的值:" + boundListOperations.leftPop());
//弹出右边的值,b
        System.out.println("弹出右边的值:" + boundListOperations.rightPop());
//在指定的出现第一个值左边添加值
        boundListOperations.leftPush("i","w");
        boundListOperations.range(0,-1).forEach(v -> System.out.println("在指定的出现第一个值左边添加值：" + v));
        //    在指定的第一个值出现的右边添加值
        boundListOperations.rightPush("i","x");
        boundListOperations.range(0,-1).forEach(v -> System.out.println("在指定的出现第一个值左边添加值：" + v));
        //在指定的时间过后弹出左边的值
        boundListOperations.leftPop(1, TimeUnit.SECONDS);
        //在指定的时间过后弹出右边的值
        boundListOperations.rightPop(1, TimeUnit.SECONDS);
        //在左边批量添加值
        boundListOperations.leftPushAll("y","g");
        boundListOperations.range(0,-1).forEach(v -> System.out.println("在左边批量添加值：" + v));
        //在右边批量添加值
        boundListOperations.rightPushAll("b","r");
        boundListOperations.range(0,-1).forEach(v -> System.out.println("在右边批量添加值：" + v));
        //向左边添加不存在的值
        boundListOperations.leftPushIfPresent("k");
        boundListOperations.leftPushIfPresent(";");
        boundListOperations.range(0,-1).forEach(v -> System.out.println("向左边添加不存在的值：" + v));
        //向右边添加不存在的值
        boundListOperations.rightPushIfPresent("k");
        boundListOperations.rightPushIfPresent(";");
        System.out.println("向右边添加不存在的值：" + boundListOperations.range(0,-1).toString());
//移除指定值的个数
        long removeCount = boundListOperations.remove(2,"i");
        System.out.println("移除指定值的个数:" + removeCount);
        System.out.println("移除指定值的个数后剩余的值：" + boundListOperations.range(0,-1).toString());
//在指定位置设置值
        boundListOperations.set(0,"j");
        System.out.println("在指定位置设置值：" + boundListOperations.range(0,-1).toString());
        //截取指定区间位置的值
        boundListOperations.trim(1,3);
        System.out.println("截取指定区间位置的值：" + boundListOperations.range(0,-1).toString());
        return true;
    }


    /**
     * window 运行高版本redis   https://zhuanlan.zhihu.com/p/496391149
     * 是一个新的强大的支持多播的可持久化的消息队列
     * Redis 流（Stream）方法使用详解 https://blog.csdn.net/Alian_1223/article/details/128443237
     *
     * @param key
     */
    @RequestMapping("/opsForStream")
    public void opsForStream(String key) {
//        add();
//        rangeAndRevRange();
//        xTrim();
//        xDel();
//        xLen();
//        xRead();
//        xGroup();
//        xReadGroup();
//        xPending();
//        xAck();
//        xClaim();
//        xInfo();
//        String strea = redisStream.add("strea", "996", "996");
//        System.out.println(strea);
//        创建Stream
        StreamOperations streamOperations = redisTemplate.opsForStream();
        //生产者发送消息  "emailStream"-是消费者配置的Stream队列名称
        HashMap<String,Object> objectHashMap=new HashMap<>();
        objectHashMap.put(key,key);
        streamOperations.add("stream1",objectHashMap);


    }

    /**XADD 向队列添加消息，如果指定的队列不存在，则创建一个队列
     * xadd stream1 * name alian age 28
     * 这里的 * 表示redis自动生成流ID，比如这里生成的是 1672034159754-0
     */
    public void add() {
        String redisKey = "stream1";
        // 给流stream1，添加新元素
        Map<String, String> record = new HashMap<>();
        record.put("name", "alian");
        record.put("age", "28");
        redisTemplate.opsForStream().add(redisKey, record);
        List<MapRecord<String, Object, Object>> range = redisTemplate.opsForStream().range(redisKey, Range.unbounded());
        System.out.println("获取到的流的信息：{}"+range);
    }

    /**
     * XRANGE 获取流中的数据（- 表示最小值，+表示最大值）
     * XREVRANGE 逆序获取流中的数据（- 表示最小值，+表示最大值）
     * XRANGE STREAM_NAME start end [COUNT count]
     * XREVRANGE STREAM_NAME end start [COUNT count]
     *
     * 127.0.0.1:6379> xadd stream2 * name alian
     * "1672036792167-0"
     * 127.0.0.1:6379> xadd stream2 * age 28
     * "1672036804362-0"
     * 127.0.0.1:6379> xrange stream2 - +
     * 1) 1) "1672036792167-0"
     *    2) 1) "name"
     *       2) "alian"
     * 2) 1) "1672036804362-0"
     *    2) 1) "age"
     *       2) "28"
     * 127.0.0.1:6379> xrevrange stream2 + -
     * 1) 1) "1672036804362-0"
     *    2) 1) "age"
     *       2) "28"
     * 2) 1) "1672036792167-0"
     *    2) 1) "name"
     *       2) "alian"
     * 127.0.0.1:6379> xrange stream2 - + COUNT 1
     * 1) 1) "1672036792167-0"
     *    2) 1) "name"
     *       2) "alian"
     *向流（stream2）中添加消息，字段 name，值 alian
     * 向流（stream2）中添加消息，字段 age，值 28
     * 获取流（stream2）中所有的数据
     * 逆序获取流（stream2）中所有的数据
     */
    public void rangeAndRevRange() {
        String redisKey = "stream2";
        // 给流stream2，添加新元素
        Map<String, String> record1 = new HashMap<>();
        record1.put("name", "alian");
        redisTemplate.opsForStream().add(redisKey, record1);
        // 给流stream2，添加新元素
        Map<String, String> record2 = new HashMap<>();
        record2.put("age", "28");
        redisTemplate.opsForStream().add(redisKey, record2);
        List<MapRecord<String, Object, Object>> range = redisTemplate.opsForStream().range(redisKey, Range.unbounded());
        System.out.println("获取到的流的信息：{}"+range);
        List<MapRecord<String, Object, Object>> revRange = redisTemplate.opsForStream().reverseRange(redisKey, Range.unbounded());
        System.out.println("逆序获取到的流的信息：{}"+revRange);
        List<MapRecord<String, Object, Object>> rangeLimit = redisTemplate.opsForStream().range(redisKey, Range.unbounded(), RedisZSetCommands.Limit.limit().count(1));
        System.out.println("获取指定数量的流的信息：{}"+rangeLimit);
    }

    /**
     * XTRIM STREAM_NAME MAXLEN [~] len
     * xtrim stream3 MAXLEN 2
     * 使用（xtrim ）对流进行修剪，限制长度为2
     */
    public void xTrim() {
        String redisKey = "stream3";
        // 给流stream3，添加新元素
        Map<String, String> record1 = new HashMap<>();
        record1.put("name", "alian");
        redisTemplate.opsForStream().add(redisKey, record1);
        Map<String, String> record2 = new HashMap<>();
        record2.put("age", "28");
        redisTemplate.opsForStream().add(redisKey, record2);
        Map<String, String> record3 = new HashMap<>();
        record3.put("sex", "男");
        redisTemplate.opsForStream().add(redisKey, record3);
        // 对流进行裁剪
        Long trim = redisTemplate.opsForStream().trim(redisKey, 2);
        log.info("对流进行裁剪返回结果：{}",trim);
        // 获取流的信息
        List<MapRecord<String, Object, Object>> range = redisTemplate.opsForStream().range(redisKey, Range.unbounded());
        log.info("获取到的流的信息：{}",range);
    }

    /**
     * XDEL 移除指定元素
     * XDEL STREAM_NAME STREAM_ID [STREAM_ID  ...]
     * xdel stream4 "1672039298619-0" "1672039305185-0"
     * 使用（xdel ）移除流中ID为， 1672039298619-0和 1672039305185-0 的元素
     */
    public void xDel() {
        String redisKey = "stream4";
        // 给流stream3，添加新元素
        Map<String, String> record1 = new HashMap<>();
        record1.put("name", "alian");
        RecordId recordId1 = redisTemplate.opsForStream().add(redisKey, record1);
        Map<String, String> record2 = new HashMap<>();
        record2.put("age", "28");
        RecordId recordId2 = redisTemplate.opsForStream().add(redisKey, record2);
        Map<String, String> record3 = new HashMap<>();
        record3.put("sex", "男");
        RecordId recordId3 = redisTemplate.opsForStream().add(redisKey, record3);
        // 删除后面两个流元素
        Long delete = redisTemplate.opsForStream().delete(redisKey, recordId2, recordId3);
        // 获取流的信息
        List<MapRecord<String, Object, Object>> range = redisTemplate.opsForStream().range(redisKey, Range.unbounded());
        log.info("获取到的流的信息：{}", range);
    }

    /**
     * XLEN 获取流包含的元素数量，即消息长度
     * XLEN STREAM_NAME
     * xlen stream5
     * 使用（xlen ）获取流包含的元素数量，得到 3
     *
     */
    public void xLen() {
        String redisKey = "stream5";
        // 给流stream3，添加新元素
        Map<String, String> record1 = new HashMap<>();
        record1.put("name", "alian");
        redisTemplate.opsForStream().add(redisKey, record1);
        Map<String, String> record2 = new HashMap<>();
        record2.put("age", "28");
        redisTemplate.opsForStream().add(redisKey, record2);
        Map<String, String> record3 = new HashMap<>();
        record3.put("sex", "男");
        redisTemplate.opsForStream().add(redisKey, record3);
        // 获取流包含的元素数量
        Long size = redisTemplate.opsForStream().size(redisKey);
        log.info("获取流包含的元素数量：{}", size);
        // 获取流的信息
    }

    /**
     * XREAD 以阻塞或非阻塞方式获取流元素
     * XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
     * xread count 3 STREAMS stream6 0-0
     * 使用（xread ）非阻塞的方式获取流（stream6）中 3 个流元素
     * xread count 2 BLOCK 10000  STREAMS stream6 0-0
     * 使用（xread ）阻塞的方式获取流（stream6）中 2 个流元素，超时时间为 10000毫秒
     */
    public void xRead() {
        String redisKey = "stream6";
        // 给流stream6添加新元素
        Map<String, String> record1 = new HashMap<>();
        record1.put("name", "alian");
        redisTemplate.opsForStream().add(redisKey, record1);
        Map<String, String> record2 = new HashMap<>();
        record2.put("age", "28");
        redisTemplate.opsForStream().add(redisKey, record2);

        Map<String, String> record3 = new HashMap<>();
        record3.put("goods", "UAV");
        redisTemplate.opsForStream().add(redisKey, record3);
        Map<String, String> record4 = new HashMap<>();
        record4.put("price", "99.9");
        redisTemplate.opsForStream().add(redisKey, record4);

        // 获取流包含的元素数量
        List<MapRecord<String, Object, Object>> result1 = redisTemplate.opsForStream().read(StreamReadOptions.empty().count(3), StreamOffset.fromStart(redisKey));
        log.info("获取流包含的元素数量：{}", result1);
        // 阻塞方式读取流元素
        List<MapRecord<String, Object, Object>> result2 = redisTemplate.opsForStream().read(StreamReadOptions.empty().block(Duration.ofMillis(10 * 1000)).count(2), StreamOffset.fromStart(redisKey));
        log.info("阻塞方式读取流元素：{}", result2);
    }

    /**
     * XGROUP CREATE 为流创建一个具有指定名字的消费者组
     * XGROUP SETID 修改消费者组的最后递送消息ID
     * #STREAM_NAME存在
     * XGROUP create STREAM_NAME GROUP_NAME 0
     * #STREAM_NAME不存在
     * XGROUP create STREAM_NAME GROUP_NAME $ MKSTREAM
     * 127.0.0.1:6379> xgroup create stream7 group1 $ MKSTREAM
     * OK
     * 127.0.0.1:6379> xgroup create stream7 group2 0
     * OK
     * 127.0.0.1:6379> xgroup create stream7 group3 0
     * OK
     * 127.0.0.1:6379> xinfo groups stream7
     * 1) 1) "name"
     *    2) "group1"
     *    3) "consumers"
     *    4) (integer) 0
     *    5) "pending"
     *    6) (integer) 0
     *    7) "last-delivered-id"
     *    8) "0-0"
     * 2) 1) "name"
     *    2) "group2"
     *    3) "consumers"
     *    4) (integer) 0
     *    5) "pending"
     *    6) (integer) 0
     *    7) "last-delivered-id"
     *    8) "0-0"
     * 3) 1) "name"
     *    2) "group3"
     *    3) "consumers"
     *    4) (integer) 0
     *    5) "pending"
     *    6) (integer) 0
     *    7) "last-delivered-id"
     *    8) "0-0"
     *
     */
    public void xGroup() {
        String redisKey = "stream7";
        String group1 = redisTemplate.opsForStream().createGroup(redisKey, "group1");
        log.info("创建第一个组返回：{}",group1);
        String group2 = redisTemplate.opsForStream().createGroup(redisKey, "group2");
        log.info("创建第二个组返回：{}",group2);
        String group3 = redisTemplate.opsForStream().createGroup(redisKey, "group3");
        log.info("创建第三个组返回：{}",group3);
        StreamInfo.XInfoGroups groups = redisTemplate.opsForStream().groups(redisKey);
        log.info("获取组的信息：{}",groups);
    }


    /**
     * XREADGROUP 读取消费者组中的消息
     * XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]
     *127.0.0.1:6379> xgroup create stream8 group1 $ mkstream
     * OK
     * 127.0.0.1:6379> multi
     * OK
     * 127.0.0.1:6379> xadd stream8 * a 1
     * QUEUED
     * 127.0.0.1:6379> xadd stream8 * a 2
     * QUEUED
     * 127.0.0.1:6379> xadd stream8 * a 3
     * QUEUED
     * 127.0.0.1:6379> exec
     * 1) "1672128676947-0"
     * 2) "1672128676947-1"
     * 3) "1672128676947-2"
     * 127.0.0.1:6379> xreadgroup group group1 consumer1 count 1 streams stream8 >
     * 1) 1) "stream8"
     *    2) 1) 1) "1672128676947-0"
     *          2) 1) "a"
     *             2) "1"
     * 127.0.0.1:6379> xreadgroup group group1 consumer2 count 1 streams stream8 >
     * 1) 1) "stream8"
     *    2) 1) 1) "1672128676947-1"
     *          2) 1) "a"
     *             2) "2"
     * 127.0.0.1:6379> xreadgroup group group1 consumer1 count 1 streams stream8 >
     * 1) 1) "stream8"
     *    2) 1) 1) "1672128676947-2"
     *          2) 1) "a"
     *             2) "3"
     * 127.0.0.1:6379> xreadgroup group group1 consumer2 count 1 streams stream8 >
     * (nil)
     */
    public void xReadGroup() {
        String redisKey = "stream8";
//        String group1 = redisTemplate.opsForStream().createGroup(redisKey, "group1");
//        log.info("创建第一个组返回：{}", group1);
        // 消费者
        Consumer consumer = Consumer.from("group1", "consumer1");
        while (true) {
            // 读取消息
            List<MapRecord<String, Object, Object>> list = redisTemplate.opsForStream().read(consumer, StreamReadOptions.empty().count(1).block(Duration.ofSeconds(5)), StreamOffset.create(redisKey, ReadOffset.lastConsumed()));
            log.info("读取到的信息：{}", list);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * XPENDING 显示待处理消息的相关信息
     * XPENDING key group [[IDLE min-idle-time] start end count [consumer]]
     * xpending stream9 group1
     * xpending stream9 group1 - + 2     #最多取出2条待处理的相关信息
     * xpending stream9 group1 - + 3      #最多取出3条待处理的相关信息
     */
    public void xPending() {
        String redisKey = "stream9";
//        String group1 = redisTemplate.opsForStream().createGroup(redisKey, "group1");
//        log.info("创建第一个组返回：{}", group1);
        PendingMessagesSummary pendingSummary = redisTemplate.opsForStream().pending(redisKey, "group1");
        log.info("显示待处理的消息：{}", pendingSummary);
        PendingMessages pendingMessages = redisTemplate.opsForStream().pending(redisKey, "group1", Range.unbounded(), 2);
        log.info("最多显示2条待处理的消息：{}", pendingMessages);
        PendingMessages pending = redisTemplate.opsForStream().pending(redisKey, Consumer.from("group1", "consumer2"));
        log.info("显示给定消费者2待处理的相关信息：{}", pending);
    }

    /**
     * XACK 将消息标记为“已处理”
     *XACK STREAM_NAME group id [id ...]
     * xack stream9 group1 1672190304546-2
     */


    public void xAck() {
        String redisKey = "stream9";
        // 消费者2的消息进行标记
        Long acknowledge = redisTemplate.opsForStream().acknowledge(redisKey, "group1", "1687682472539-0");
        log.info("将消息标记为已处理：{}", acknowledge);
        PendingMessagesSummary pendingSummary = redisTemplate.opsForStream().pending(redisKey, "group1");
        log.info("显示待处理的消息：{}", pendingSummary);
    }

    /**
     * XCLAIM 转移消息的归属权
     * xclaim stream9 group1 consumer2 5000 1672190304546-0
     */
    public void xClaim() {
        List<ByteRecord> recordList = redisTemplate.execute((RedisCallback<List<ByteRecord>>) connection -> {
            // XCLAIM指令的实现
            return connection.streamCommands().xClaim("stream9".getBytes(), "group1", "consumer2", Duration.ofSeconds(10), RecordId.of("1687682472539-1"));
        });
        if (recordList != null) {
            for (ByteRecord byteRecord : recordList) {
                log.info("修改消息的消费者：id={}, value={}", byteRecord.getId(), byteRecord.getValue());
            }
        }
    }


    /**
     * XINFO GROUPS 打印消费组信息
     * XINFO CONSUMERS 打印消费者信息
     * XINFO STREAM 打印流信息
     * XINFO GROUPS key
     * XINFO CONSUMERS key groupname
     * XINFO STREAM key [FULL [COUNT count]]
     * xinfo  groups stream9
     * xinfo consumers stream9 group1
     * xinfo stream stream9
     */
    public void xInfo() {
        String redisKey = "stream9";
        // 消费者2的消息进行标记
        StreamInfo.XInfoGroups groups = redisTemplate.opsForStream().groups(redisKey);
        log.info("获取组的信息：{}", groups);
        StreamInfo.XInfoConsumers consumers = redisTemplate.opsForStream().consumers(redisKey, "group1");
        log.info("获取消费者的信息：{}", consumers);
        StreamInfo.XInfoStream infoStream = redisTemplate.opsForStream().info(redisKey);
        log.info("获取流的信息：{}", infoStream);
    }



    @RequestMapping("/opsForStream1")
    public void opsForStream1(String key) {
        StreamOperations<String, Object, Object> stringObjectObjectStreamOperations = redisTemplate.opsForStream(new HashMapper<String, Object, Object>() {
            @Override
            public Map<Object, Object> toHash(String object) {
                return null;
            }

            @Override
            public String fromHash(Map<Object, Object> hash) {
                return null;
            }
        });
    }

    @RequestMapping("/boundStreamOps")
    public void boundStreamOps(String key) {
        BoundStreamOperations<String, Object, Object> stringObjectObjectBoundStreamOperations = redisTemplate.boundStreamOps(key);
    }




    @RequestMapping("/opsForValue")
    public void opsForValue(String key) throws InterruptedException {
        redisTemplate.opsForValue().set("stringValue","bbb");
        //获取key键对应的值。
        String stringValue = redisTemplate.opsForValue().get("stringValue")+"";
        System.out.println("通过get(Object key)方法获取set(K key, V value)方法新增的字符串值:" + stringValue);
        //在原有的值基础上新增字符串到末尾。
        redisTemplate.opsForValue().append("stringValue","aaa");
        String stringValueAppend = redisTemplate.opsForValue().get("stringValue")+"";
        System.out.println("通过append(K key, String value)方法修改后的字符串:"+stringValueAppend);
        //截取key键对应值得字符串，从开始下标位置开始到结束下标的位置(包含结束下标)的字符串。
        String cutString = redisTemplate.opsForValue().get("stringValue",0,3);
        System.out.println("通过get(K key, long start, long end)方法获取截取的字符串:"+cutString);
        //     获取原来key键对应的值并重新赋新值。
        String oldAndNewStringValue = redisTemplate.opsForValue().getAndSet("stringValue","ccc")+"";
        System.out.print("通过getAndSet(K key, V value)方法获取原来的" + oldAndNewStringValue + ",");
        String newStringValue = redisTemplate.opsForValue().get("stringValue")+"";
        System.out.println("修改过后的值:"+newStringValue);
        //key键对应的值value对应的ascii码,在offset的位置(从左向右数)变为value。
        redisTemplate.opsForValue().setBit("stringValue",1,false);
        newStringValue = redisTemplate.opsForValue().get("stringValue")+"";
        System.out.println("通过setBit(K key,long offset,boolean value)方法修改过后的值:"+newStringValue);
        //     判断指定的位置ASCII码的bit位是否为1。
        boolean bitBoolean = redisTemplate.opsForValue().getBit("stringValue",1);
        System.out.println("通过getBit(K key,long offset)方法判断指定bit位的值是:" + bitBoolean);
        //     获取指定字符串的长度。
        Long stringValueLength = redisTemplate.opsForValue().size("stringValue");
        System.out.println("通过size(K key)方法获取字符串的长度:"+stringValueLength);
        // 以增量的方式将double值存储在变量中。
        double stringValueDouble = redisTemplate.opsForValue().increment("doubleValue",5);
        System.out.println("通过increment(K key, double delta)方法以增量方式存储double值:" + stringValueDouble);
        //以增量的方式将long值存储在变量中。
        double stringValueLong = redisTemplate.opsForValue().increment("longValue",6);
        System.out.println("通过increment(K key, long delta)方法以增量方式存储long值:" + stringValueLong);
        //      如果键不存在则新增,存在则不改变已经有的值。
        boolean absentBoolean = redisTemplate.opsForValue().setIfAbsent("absentValue","fff");
        System.out.println("通过setIfAbsent(K key, V value)方法判断变量值absentValue是否存在:" + absentBoolean);
        if(absentBoolean){
            String absentValue = redisTemplate.opsForValue().get("absentValue")+"";
            System.out.print(",不存在，则新增后的值是:"+absentValue);
            boolean existBoolean = redisTemplate.opsForValue().setIfAbsent("absentValue","eee");
            System.out.print(",再次调用setIfAbsent(K key, V value)判断absentValue是否存在并重新赋值:" + existBoolean);
            if(!existBoolean){
                absentValue = redisTemplate.opsForValue().get("absentValue")+"";
                System.out.print("如果存在,则重新赋值后的absentValue变量的值是:" + absentValue);
            }
        }
        //       设置变量值的过期时间。
        redisTemplate.opsForValue().set("timeOutValue","timeOut",5,TimeUnit.SECONDS);
        String timeOutValue = redisTemplate.opsForValue().get("timeOutValue")+"";
        System.out.println("通过set(K key, V value, long timeout, TimeUnit unit)方法设置过期时间，过期之前获取的数据:"+timeOutValue);
        Thread.sleep(5*1000);
        timeOutValue = redisTemplate.opsForValue().get("timeOutValue")+"";
        System.out.print(",等待10s过后，获取的值:"+timeOutValue);
        //  覆盖从指定位置开始的值。
        redisTemplate.opsForValue().set("absentValue","dd",1);
        String overrideString = redisTemplate.opsForValue().get("absentValue")+"";
        System.out.println("通过set(K key, V value, long offset)方法覆盖部分的值:"+overrideString);
        //  设置map集合到redis。
        Map valueMap = new HashMap();
        valueMap.put("valueMap1","map1");
        valueMap.put("valueMap2","map2");
        valueMap.put("valueMap3","map3");
        redisTemplate.opsForValue().multiSet(valueMap);
        //  根据集合取出对应的value值。
        List paraList = new ArrayList();
        paraList.add("valueMap1");
        paraList.add("valueMap2");
        paraList.add("valueMap3");
        List<String> valueList = redisTemplate.opsForValue().multiGet(paraList);
        for (String value : valueList){
            System.out.println("通过multiGet(Collection<K> keys)方法获取map值:" + value);
        }
        //  如果对应的map集合名称不存在，则添加，如果存在则不做修改。
         valueMap = new HashMap();
        valueMap.put("valueMap1","map1");
        valueMap.put("valueMap2","map2");
        valueMap.put("valueMap3","map3");
        redisTemplate.opsForValue().multiSetIfAbsent(valueMap);
    }




    /**
     * 获取key序列化程序
     * @param key
     * @return
     */
    @RequestMapping("/getKeySerializer")
    public RedisSerializer<?> getKeySerializer(String key) {
        RedisSerializer<?> keySerializer = redisTemplate.opsForValue().getOperations().getKeySerializer();
        return keySerializer;
    }


    /**
     * 获取hash key序列化程序
     * @param key
     * @return
     */
    @RequestMapping("/getHashKeySerializer")
    public Object getHashKeySerializer(String key) {
        SortQuery<String> defaultSortQuery= SortQueryBuilder.sort(key).build();
//        new DefaultSortQuery(key, SortParameters.Order.DESC,true,new SortParameters.Range(0,10),"sd",null);
        RedisSerializer<?> hashKeySerializer = redisTemplate.opsForValue().getOperations().getHashKeySerializer();
        return hashKeySerializer;
    }

    /**
     * 获得hash value序列化
     * @param key
     * @return
     */
    @RequestMapping("/getHashValueSerializer")
    public Object getHashValueSerializer(String key) {
        RedisSerializer<?> hashValueSerializer = redisTemplate.opsForValue().getOperations().getHashValueSerializer();
        return hashValueSerializer;
    }

    /**
     * 获得value的序列化
     * @param key
     * @return
     */

    @RequestMapping("/getValueSerializer")
    public Object getValueSerializer(String key) {
        RedisSerializer<?> valueSerializer = redisTemplate.opsForValue().getOperations().getValueSerializer();
        return valueSerializer;
    }






    @RequestMapping("/execRedisSerializer")
    public List<Object> execRedisSerializer(String key) {
        //设置启用事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //标记事务块的开始。命令将被排队，然后可以通过调用exec（）执行或使用discard（）回滚
        redisTemplate.multi();
        redisTemplate.opsForValue().set(key, "xds");

        return redisTemplate.opsForValue().getOperations().exec(new Jackson2JsonRedisSerializer<>(Object.class));
    }














    /**
     * https://blog.csdn.net/qq_41853447/article/details/113095144
     *
     *
     *
     * zset数据类型，增加地理位置
     * id         score                        member
     * 1          4024943625713994             "这个位置的名称"
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsAdd")
    public Object boundGeoOpsAdd(String key) {
        // x:经度，Y:维度
        Point point = new Point(106.63658, 26.653324);
// outlets 集合名称
        //key：集合名称
        //
        //var1：位置信息
        //
        //var2：位置名称
        redisTemplate.boundGeoOps("outlets")
                .add(point, "这个位置的名称");
        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }

    /**
     * zset 增加多个地理位置
     * ID     score                             member
     * 1        4026417408679994                "位置1"
       2        4024962223812401                "位置名称"
       3        4024947630466140                "位置2"
       4        4024943625713994                "这个位置的名称"
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsMapAdd")
    public Object boundGeoOpsMapAdd(String key) {
        // 创建Map集合保存多个位置
        Map<Object,Point> map = new HashMap<>();
        map.put("位置名称",new Point(106.777434,26.925769));
        map.put("位置1",new Point(106.944735,26.888657));
        map.put("位置2",new Point(106.623357,26.781373));
// 批量添加位置信息
        redisTemplate.boundGeoOps("outlets")
                .add(map);

        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }

    /**
     * 根据位置名称查询坐标信息
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsPosition")
    public Object boundGeoOpsPosition(String key) {
        List<Point> position = redisTemplate.boundGeoOps("outlets").position("这个位置的名称");
        System.out.println(position);
        List<Point> list = redisTemplate.boundGeoOps("outlets").position("位置名称", "位置1", "位置2");
        System.out.println(list);

        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }






    /**
     * 计算两点间距离
     * key：集合名称
     *
     * M：位置信息，可以传输多个。就是保存位置时保存的位置名称，保存位置的名称是什么类型，这里就是什么类型。
     *
     * var3：返回距离的单位，默认为米。可以使用 org.springframework.data.geo.Metrics枚举类。 Metrics.KILOMETERS：千米
     *
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsDistance")
    public Object boundGeoOpsDistance(String key) {
        // 直接这样计算，默认单位为 米M
        Distance distance = redisTemplate.boundGeoOps("outlets").distance("位置1", "位置2");
// 计算两两点间的距离，并单位设置为千米。最后这个参数就是设置单位的。
        Distance distance1 = redisTemplate.boundGeoOps("outlets").distance("位置1", "位置2", Metrics.KILOMETERS);
// 距离
        double value = distance.getValue();
        double value1 = distance1.getValue();
// 单位
        String unit = distance1.getUnit();
        System.out.println("两地之间的距离："+value+"米，"+value1+unit);


        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }

    /**
     * 根据一个坐标查询附近位置
     *
     * var1：设置当前经纬度坐标和需要查询附近多少距离和单位。
     *
     * var2：设置返回值的数据。
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsRadius")
    public Object boundGeoOpsRadius(String key) {
// 指定经度和维度
        Point point = new Point(106.6682, 26.896905);
// 指定距离和单位，目前是100千米km
        Distance distance = new Distance(100, Metrics.KILOMETERS);
        Circle circle = new Circle(point, distance);
// RedisGeoCommands.GeoLocation <String> 中的String 类型就是保存位置时填写的那个位置名称的数据类型
// .radius(circle); 只给 Circle 参数，那么只会返回一个网点名称，其余的参数都没有
//        GeoResults<RedisGeoCommands.GeoLocation<String>> outlets = redisTemplate.boundGeoOps("outlets").radius(circle);


// 指定返回的数据,当前这个点与附近这些点的距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.sortAscending() // 升序排序
                //                .sortDescending() // 降序排序
                .includeCoordinates() // 包含坐标信息信息
                .includeDistance() // 包含距离信息
                .limit(10); // 显示返回数量
// 根据坐标查询，并设置返回的参数
        GeoResults<RedisGeoCommands.GeoLocation<Object>> outlets = redisTemplate.boundGeoOps("outlets").radius(circle,args);
// 循环输出
        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> result : outlets){

            // 返回网点信息
            RedisGeoCommands.GeoLocation<Object> location = result.getContent();
            // 网点名
            String name = location.getName().toString();
            // 网点坐标
            Point point1 = location.getPoint();

            // 返回距离
            Distance dis = result.getDistance();
            // 距离
            double value = dis.getValue();
            // 单位
            String unit = dis.getUnit();

            // 输出
            System.out.println("网点名："+ name + ",x坐标"+point1.getX() + ",y坐标" + point1.getY() + ",距离：" + value + ",单位：" + unit);
        }

        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }

    /**
     * key：集合名称
     *
     * M：位置名称，添加位置保存的那个名称。
     *
     * var2：设置距离和单位。
     *
     * var3：设置返回数据
     * @param key
     * @return
     */
    @RequestMapping("/boundGeoOpsRadiusByName")
    public Object boundGeoOpsRadiusByName(String key) {
// 50 千米
        Distance distance = new Distance(50, Metrics.KILOMETERS);
// 指定返回的数据,当前这个点与附近这些点的距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.sortAscending() // 升序排序
                //                .sortDescending() // 降序排序
                .includeCoordinates() // 包含坐标信息信息
                .includeDistance() // 包含距离信息
                .limit(10); // 显示返回数量
// 根据坐标查询，并设置返回的参数
// 只是 .radius("位置1",distance,args); 这里传输参数和testGeoRadius() 方法不同，其余全部一样。
// 注意：查询这个名称一定要在集合中存在。如果不存在查询就会报异常  org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR could not decode requested zset member
        GeoResults<RedisGeoCommands.GeoLocation<Object>> outlets = redisTemplate.boundGeoOps("outlets").radius("位置2",distance,args);

// 循环输出
        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> result : outlets){

            // 返回网点信息
            RedisGeoCommands.GeoLocation<Object> location = result.getContent();
            // 网点名
            String name = location.getName().toString();
            // 网点坐标
            Point point1 = location.getPoint();

            // 返回距离
            Distance dis = result.getDistance();
            // 距离
            double value = dis.getValue();
            // 单位
            String unit = dis.getUnit();

            // 输出
            System.out.println("网点名："+ name + "x坐标"+point1.getX() + "y坐标" + point1.getY() + "距离：" + value + "单位：" + unit);
        }

        return redisTemplate.opsForValue().getOperations().boundGeoOps("outlets");
    }















    @RequestMapping("/boundValueOps")
    public Object boundValueOps(String key) {
        BoundValueOperations boundValueOperations = redisTemplate.boundValueOps("bvo");
        boundValueOperations.append("a");
        boundValueOperations.append("b");
        //获取从指定位置开始，到指定位置为止的值
        System.out.println("获取从指定位置开始，到指定位置为止的值:" + boundValueOperations.get(0,2));
        //获取所有值
        System.out.println("获取所有值:" + boundValueOperations.get());
        ////重新设置值
        boundValueOperations.set("f");
        System.out.println("重新设置值:" + boundValueOperations.get());
        //在指定时间后重新设置
        boundValueOperations.set("wwww",5,TimeUnit.SECONDS);
        System.out.println("在指定时间后重新设置:" + boundValueOperations.get());
        //截取原有值的指定长度后添加新值在后面
        boundValueOperations.set("nnnnnn",3);
        System.out.println("截取原有值的指定长度后添加新值在后面:" + boundValueOperations.get());
//没有值存在则添加
        boundValueOperations.setIfAbsent("mmm");
        System.out.println("没有值存在则添加:" + boundValueOperations.get());
        //获取原来的值，并覆盖为新值
        Object object = boundValueOperations.getAndSet("yyy");
        System.out.print("获取原来的值" + object);
        System.out.println("，覆盖为新值:" + boundValueOperations.get());
        System.out.println("value值的长度:" + boundValueOperations.size());
        //自增长只能在为数字类型的时候才可以
        boundValueOperations.set(1L);
        boundValueOperations.increment(1);
        System.out.println("自增长只能在为数字类型的时候才可以:" + boundValueOperations.get());
  return redisTemplate.opsForValue().getOperations().boundValueOps(key);
    }

    @RequestMapping("/boundZSetOps")
    public Object boundZSetOps(String key) {
        BoundZSetOperations boundZSetOperations = redisTemplate.boundZSetOps("{bzso}");
        //绑定键中添加值，同时指定值的分数
        boundZSetOperations.add("a",1);
        boundZSetOperations.add("b",2);
        //获取绑定键的指定区间值
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("集合中的值:" + v));
        //获取从指定位置开始(下标起始坐标为1)到结束位置为止的值个数
        System.out.println("获取从指定位置开始(下标起始坐标为1)到结束位置为止的值个数:" + boundZSetOperations.count(1,2));
        //通过TypedTuple方式新增数据
        ZSetOperations.TypedTuple typedTuple1 = new DefaultTypedTuple("E",6.0);
        ZSetOperations.TypedTuple typedTuple2 = new DefaultTypedTuple("F",7.0);
        ZSetOperations.TypedTuple typedTuple3 = new DefaultTypedTuple("G",5.0);
        Set<ZSetOperations.TypedTuple> typedTupleSet = new HashSet<ZSetOperations.TypedTuple>();
        typedTupleSet.add(typedTuple1);
        typedTupleSet.add(typedTuple2);
        typedTupleSet.add(typedTuple3);
        boundZSetOperations.add(typedTupleSet);
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("通过TypedTuple方式新增数据:" + v));
        //自增长后的数据
        boundZSetOperations.incrementScore("a",1);
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("自增长后的数据:" + v));
//获取相同值，并存储
        boundZSetOperations.intersectAndStore("{bzso}1","{bzso}2");
        redisTemplate.opsForZSet().range("{bzso}2",0,-1).forEach(v -> System.out.println("获取相同值，并存储" + v));
        // 匹配获取键值对，ScanOptions.NONE为获取全部键值对；ScanOptions.scanOptions().match("C").build()匹配获取键位map1的键值对,不能模糊匹配。
        Cursor<ZSetOperations.TypedTuple> cursor = boundZSetOperations.scan(ScanOptions.NONE);
        while (cursor.hasNext()){
             ZSetOperations.TypedTuple typedTuple = cursor.next();
             System.out.println("扫描绑定数据:" + typedTuple.getValue() + "—>" + typedTuple.getScore());
            }
        //按照值来排序的取值,这个排序只有在有相同分数的情况下才能使用，如果有不同的分数则返回值不确定
        RedisZSetCommands.Range range = new RedisZSetCommands.Range();
        range.lte("b");
        //range.gte("F");
        boundZSetOperations.rangeByLex(range).forEach(v -> System.out.println("按照值来排序的取值:" + v));
        //获取从设置下标开始的设置长度的元素值
        RedisZSetCommands.Limit limit = new RedisZSetCommands.Limit();
        limit.count(2);
        //起始下标为0
        limit.offset(1);
        boundZSetOperations.rangeByLex(range,limit).forEach(v -> System.out.println("按照值来排序的限定取值:" + v));
        //按照分数排序
        boundZSetOperations.rangeByScore(3,7).forEach(v -> System.out.println("按照分数排序:" + v));
        //按照位置排序取值和分数
        Set<ZSetOperations.TypedTuple> tupleSet = boundZSetOperations.rangeWithScores(3,5);
        tupleSet.forEach(v -> System.out.printf("按照位置排序取值和分数:%s–>%s\n",v.getValue(),v.getScore()));
        //按照分数位置排序取值和分数
        Set<ZSetOperations.TypedTuple> scoreSet = boundZSetOperations.rangeByScoreWithScores(1,5);
        scoreSet.forEach(v -> System.out.printf("按照分数位置排序取值和分数:%s–>%s\n",v.getValue(),v.getScore()));
        //按照值来倒序取值
        boundZSetOperations.reverseRange(0,3).forEach(v -> System.out.println("按照值来倒序取值:" + v));
        //按照分数来倒序取值
        boundZSetOperations.reverseRangeByScore(2,5).forEach(v -> System.out.println("按照分数来倒序取值:" + v));
        //按照位置倒序取值和分数
        tupleSet = boundZSetOperations.reverseRangeWithScores(2,5);
        tupleSet.forEach(v -> System.out.printf("按照位置倒序取值和分数:%s–>%s\n",v.getValue(),v.getScore()));
        //按照分数位置倒序取值和分数
        scoreSet = boundZSetOperations.reverseRangeByScoreWithScores(2,5);
        scoreSet.forEach(v -> System.out.printf("按照分数位置倒序取值和分数:%s–>%s\n",v.getValue(),v.getScore()));
        //统计分数在某个区间的个数
        System.out.println("统计分数在某个区间的个数:" + boundZSetOperations.count(2,5));
        //获取变量中元素的索引,下标开始位置为0
        System.out.println("获取变量中元素的索引:" + boundZSetOperations.rank("b"));
        // 获取绑定键中元素的分数
        System.out.println("获取变量中元素的分数:" + boundZSetOperations.score("b"));
        //获取变量中元素的个数
        System.out.println("获取变量中元素的个数:" + boundZSetOperations.zCard());
        //比较相同的值并存储
        boundZSetOperations.intersectAndStore("{bzso}abc","{bzso}bac");
        redisTemplate.opsForSet().members("{bzso}bac").forEach(v -> System.out.println("intersectAndStore后的数据:" + v));
        //intersectAndStore集合后的数据
        List list = new ArrayList<>(Arrays.asList("{bzso}abc","{bzso}acb"));
        boundZSetOperations.intersectAndStore(list,"{bzso}bac");
        redisTemplate.opsForSet().members("{bzso}bac").forEach(v -> System.out.println("intersectAndStore集合后的数据:" + v));
        //合并所有的值并存储
        boundZSetOperations.unionAndStore("{bzso}abc","{bzso}bca");
        redisTemplate.opsForZSet().range("{bzso}bca",0,-1).forEach(v -> System.out.println("unionAndStore后的数据:" + v));

        //unionAndStore集合后的数据
        boundZSetOperations.unionAndStore(list,"{bzso}bca");
        redisTemplate.opsForZSet().range("{bzso}bca",0,-1).forEach(v -> System.out.println("unionAndStore集合后的数据:" + v));
        //移除给定值中的变量
        long removeCount = boundZSetOperations.remove("a","b");
        System.out.println("移除给定值中的变量个数:" + removeCount);
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("移除给定值中的变量后剩余的值:" + v));
        //移除区间值的变量
        boundZSetOperations.removeRange(2,3);
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("移除区间值的变量后剩余的值:" + v));
        //移除分数区间值的变量
        boundZSetOperations.removeRangeByScore(3,5);
        boundZSetOperations.range(0,-1).forEach(v -> System.out.println("移除分数区间值的变量后剩余的值:" + v));
        return redisTemplate.opsForValue().getOperations().boundZSetOps(key);
    }



















    @RequestMapping("/opsForZSet")
    public DataType opsForZSet(String key) {
       //添加元素到变量中同时指定元素的分值。
        redisTemplate.opsForZSet().add("zSetValue","A",1);
        redisTemplate.opsForZSet().add("zSetValue","B",3);
        redisTemplate.opsForZSet().add("zSetValue","C",2);
        redisTemplate.opsForZSet().add("zSetValue","D",5);
        //        获取变量指定区间的元素。
        Set zSetValue = redisTemplate.opsForZSet().range("zSetValue",0,-1);
        System.out.println("通过range(K key, long start, long end)方法获取指定区间的元素:" + zSetValue);
        //   用于获取满足非score的排序取值。这个排序只有在有相同分数的情况下才能使用，如果有不同的分数则返回值不确定。
        RedisZSetCommands.Range range = new RedisZSetCommands.Range();
//range.gt("A");
        range.lt("D");
        zSetValue = redisTemplate.opsForZSet().rangeByLex("zSetValue", range);
        System.out.println("通过rangeByLex(K key, RedisZSetCommands.Range range)方法获取满足非score的排序取值元素:" + zSetValue);
        //           用于获取满足非score的设置下标开始的长度排序取值。
        RedisZSetCommands.Limit limit = new RedisZSetCommands.Limit();
        limit.count(2);
//起始下标为0
        limit.offset(1);
        zSetValue = redisTemplate.opsForZSet().rangeByLex("zSetValue", range,limit);
        System.out.println("通过rangeByLex(K key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit)方法获取满足非score的排序取值元素:" + zSetValue);
        //   通过TypedTuple方式新增数据。
        ZSetOperations.TypedTuple<Object> typedTuple1 = new DefaultTypedTuple<Object>("E",6.0);
        ZSetOperations.TypedTuple<Object> typedTuple2 = new DefaultTypedTuple<Object>("F",7.0);
        ZSetOperations.TypedTuple<Object> typedTuple3 = new DefaultTypedTuple<Object>("G",5.0);
        Set<ZSetOperations.TypedTuple<Object>> typedTupleSet = new HashSet<ZSetOperations.TypedTuple<Object>>();
        typedTupleSet.add(typedTuple1);
        typedTupleSet.add(typedTuple2);
        typedTupleSet.add(typedTuple3);
        redisTemplate.opsForZSet().add("typedTupleSet",typedTupleSet);
        zSetValue = redisTemplate.opsForZSet().range("typedTupleSet",0,-1);
        System.out.println("通过add(K key, Set<ZSetOperations.TypedTuple<V>> tuples)方法添加元素:" + zSetValue);
        //    根据设置的score获取区间值。
        zSetValue = redisTemplate.opsForZSet().rangeByScore("zSetValue",1,2);
        System.out.println("通过rangeByScore(K key, double min, double max)方法根据设置的score获取区间值:" + zSetValue);
        //根据设置的score获取区间值从给定下标和给定长度获取最终值。
        zSetValue = redisTemplate.opsForZSet().rangeByScore("zSetValue",1,5,1,3);
        System.out.println("通过rangeByScore(K key, double min, double max, long offset, long count)方法根据设置的score获取区间值:" + zSetValue);
        //   获取RedisZSetCommands.Tuples的区间值。
         typedTupleSet = redisTemplate.opsForZSet().rangeWithScores("typedTupleSet",1,3);
        Iterator<ZSetOperations.TypedTuple<Object>> iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score = typedTuple.getScore();
            System.out.println("通过rangeWithScores(K key, long start, long end)方法获取RedisZSetCommands.Tuples的区间值:" + value + "---->" + score );
        }
        //   获取RedisZSetCommands.Tuples的区间值通过分值。
        typedTupleSet = redisTemplate.opsForZSet().rangeByScoreWithScores("typedTupleSet",5,8);
        iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score = typedTuple.getScore();
            System.out.println("通过rangeByScoreWithScores(K key, double min, double max)方法获取RedisZSetCommands.Tuples的区间值通过分值:" + value + "---->" + score );
        }
        //    获取RedisZSetCommands.Tuples的区间值从给定下标和给定长度获取最终值通过分值。
        typedTupleSet = redisTemplate.opsForZSet().rangeByScoreWithScores("typedTupleSet",5,8,1,1);
        iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score = typedTuple.getScore();
            System.out.println("通过rangeByScoreWithScores(K key, double min, double max, long offset, long count)方法获取RedisZSetCommands.Tuples的区间值从给定下标和给定长度获取最终值通过分值:" + value + "---->" + score );
        }
        //     获取区间值的个数。
        long count = redisTemplate.opsForZSet().count("zSetValue",1,5);
        System.out.println("通过count(K key, double min, double max)方法获取区间值的个数:" + count);
        //    获取变量中元素的索引,下标开始位置为0。
        long index = redisTemplate.opsForZSet().rank("zSetValue","B");
        System.out.println("通过rank(K key, Object o)方法获取变量中元素的索引:" + index);
        //          匹配获取键值对，ScanOptions.NONE为获取全部键值对；ScanOptions.scanOptions().match("C").build()匹配获取键位map1的键值对,不能模糊匹配。
        //Cursor<Object> cursor = redisTemplate.opsForSet().scan("setValue", ScanOptions.NONE);
        Cursor<ZSetOperations.TypedTuple<Object>> cursor = redisTemplate.opsForZSet().scan("zSetValue", ScanOptions.NONE);
        while (cursor.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = cursor.next();
            System.out.println("通过scan(K key, ScanOptions options)方法获取匹配元素:" + typedTuple.getValue() + "--->" + typedTuple.getScore());
        }
        //   获取元素的分值。
        double score = redisTemplate.opsForZSet().score("zSetValue","B");
        System.out.println("通过score(K key, Object o)方法获取元素的分值:" + score);
        //   获取变量中元素的个数。
        long zCard = redisTemplate.opsForZSet().zCard("zSetValue");
        System.out.println("通过zCard(K key)方法获取变量的长度:" + zCard);
        //   修改变量中的元素的分值。
        double incrementScore = redisTemplate.opsForZSet().incrementScore("zSetValue","C",5);
        System.out.print("通过incrementScore(K key, V value, double delta)方法修改变量中的元素的分值:" + incrementScore);
        score = redisTemplate.opsForZSet().score("zSetValue","C");
        System.out.print(",修改后获取元素的分值:" + score);
        zSetValue = redisTemplate.opsForZSet().range("zSetValue",0,-1);
        System.out.println("，修改后排序的元素:" + zSetValue);
        //   索引倒序排列指定区间元素。
        zSetValue = redisTemplate.opsForZSet().reverseRange("zSetValue",0,-1);
        System.out.println("通过reverseRange(K key, long start, long end)方法倒序排列元素:" + zSetValue);
        //   倒序排列指定分值区间元素。
        zSetValue = redisTemplate.opsForZSet().reverseRangeByScore("zSetValue",1,5);
        System.out.println("通过reverseRangeByScore(K key, double min, double max)方法倒序排列指定分值区间元素:" + zSetValue);
        //   倒序排列从给定下标和给定长度分值区间元素。
        zSetValue = redisTemplate.opsForZSet().reverseRangeByScore("zSetValue",1,5,1,2);
        System.out.println("通过reverseRangeByScore(K key, double min, double max, long offset, long count)方法倒序排列从给定下标和给定长度分值区间元素:" + zSetValue);
        //  倒序排序获取RedisZSetCommands.Tuples的分值区间值。
        typedTupleSet = redisTemplate.opsForZSet().reverseRangeByScoreWithScores("zSetValue",1,5);
        iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score1 = typedTuple.getScore();
            System.out.println("通过reverseRangeByScoreWithScores(K key, double min, double max)方法倒序排序获取RedisZSetCommands.Tuples的区间值:" + value + "---->" + score1 );
        }
        //  倒序排序获取RedisZSetCommands.Tuples的从给定下标和给定长度分值区间值。
        typedTupleSet = redisTemplate.opsForZSet().reverseRangeByScoreWithScores("zSetValue",1,5,1,2);
        iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score1 = typedTuple.getScore();
            System.out.println("通过reverseRangeByScoreWithScores(K key, double min, double max, long offset, long count)方法倒序排序获取RedisZSetCommands.Tuples的从给定下标和给定长度区间值:" + value + "---->" + score1 );
        }
        //   索引倒序排列区间值。
        typedTupleSet = redisTemplate.opsForZSet().reverseRangeWithScores("zSetValue",1,5);
        iterator = typedTupleSet.iterator();
        while (iterator.hasNext()){
            ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();
            Object value = typedTuple.getValue();
            double score1 = typedTuple.getScore();
            System.out.println("通过reverseRangeWithScores(K key, long start, long end)方法索引倒序排列区间值:" + value + "----->" + score1);
        }
        //   获取倒序排列的索引值。
        long reverseRank = redisTemplate.opsForZSet().reverseRank("zSetValue","B");
        System.out.println("通过reverseRank(K key, Object o)获取倒序排列的索引值:" + reverseRank);
        //   获取2个变量的交集存放到第3个变量里面。
        redisTemplate.opsForZSet().intersectAndStore("zSetValue","typedTupleSet","intersectSet");
        zSetValue = redisTemplate.opsForZSet().range("intersectSet",0,-1);
        System.out.println("通过intersectAndStore(K key, K otherKey, K destKey)方法获取2个变量的交集存放到第3个变量里面:" + zSetValue);
        //   获取多个变量的交集存放到第3个变量里面。
        List list = new ArrayList();
        list.add("typedTupleSet");
        redisTemplate.opsForZSet().intersectAndStore("zSetValue",list,"intersectListSet");
        zSetValue = redisTemplate.opsForZSet().range("intersectListSet",0,-1);
        System.out.println("通过intersectAndStore(K key, Collection<K> otherKeys, K destKey)方法获取多个变量的交集存放到第3个变量里面:" + zSetValue);
        //   获取2个变量的合集存放到第3个变量里面。
        redisTemplate.opsForZSet().unionAndStore("zSetValue","typedTupleSet","unionSet");
        zSetValue = redisTemplate.opsForZSet().range("unionSet",0,-1);
        System.out.println("通过unionAndStore(K key, K otherKey, K destKey)方法获取2个变量的交集存放到第3个变量里面:" + zSetValue);
        //   获取多个变量的合集存放到第3个变量里面。
        redisTemplate.opsForZSet().unionAndStore("zSetValue",list,"unionListSet");
        zSetValue = redisTemplate.opsForZSet().range("unionListSet",0,-1);
        System.out.println("通过unionAndStore(K key, Collection<K> otherKeys, K destKey)方法获取多个变量的交集存放到第3个变量里面:" + zSetValue);
        //   批量移除元素根据元素值。
        long removeCount = redisTemplate.opsForZSet().remove("unionListSet","A","B");
        zSetValue = redisTemplate.opsForZSet().range("unionListSet",0,-1);
        System.out.print("通过remove(K key, Object... values)方法移除元素的个数:" + removeCount);
        System.out.println(",移除后剩余的元素:" + zSetValue);
        //   根据分值移除区间元素。
        removeCount = redisTemplate.opsForZSet().removeRangeByScore("unionListSet",3,5);
        zSetValue = redisTemplate.opsForZSet().range("unionListSet",0,-1);
        System.out.print("通过removeRangeByScore(K key, double min, double max)方法移除元素的个数:" + removeCount);
        System.out.println(",移除后剩余的元素:" + zSetValue);
        //         根据索引值移除区间元素。
        removeCount = redisTemplate.opsForZSet().removeRange("unionListSet",3,5);
        zSetValue = redisTemplate.opsForZSet().range("unionListSet",0,-1);
        System.out.print("通过removeRange(K key, long start, long end)方法移除元素的个数:" + removeCount);
        System.out.println(",移除后剩余的元素:" + zSetValue);
        return redisTemplate.type(key);
    }


    /**
     * todo 还有api没有调用
     */










}
